package cn.shop.front.controller;

import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.session.SaSessionCustomUtil;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.shop.base.UserInfoForSession;
import cn.shop.front.entity.req.AppWxLogin;
import cn.shop.front.entity.req.AutWithdrawReq;
import cn.shop.front.service.*;
import cn.shop.front.util.CurrentUserUtil;
import cn.shop.transcation.TransactionRecord.entity.RechargeInfoReq;
import cn.shop.transcation.backPaymentChannelModule.entity.BackPaymentChannel;
import cn.shop.transcation.backSetting.entity.BackSetting;
import cn.shop.transcation.frontUserModule.FrontUser;
import cn.shop.transcation.login.LoginInfoReq;
import cn.shop.transcation.promotionsModule.Promotions;
import cn.shop.transcation.rechargerecord.entity.Record;
import cn.shop.utils.ApiCode;
import cn.shop.utils.ApiResult;
import cn.shop.utils.Putif;
import cn.shop.utils.constant.Constant;
import cn.shop.utils.constant.PromotionsConstant;
import cn.shop.utils.constant.SMSVerificationCode;
import cn.shop.utils.enums.TransactionState;
import cn.shop.utils.exception.ServiceException;
import cn.shop.validation.App;
import cn.shop.validation.Save;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.shop.utils.ApiResult.fail;
import static cn.shop.utils.ApiResult.result;
import static cn.shop.utils.constant.SMSVerificationCode.ADV_CODE;
import static cn.shop.utils.constant.SystemSettingConstant.*;

/**
 * app接口
 *
 * @author : 李一涵
 * @since : Created in 2022/12/5 16:39
 */
@RestController
@RequestMapping("/api/v1/app")
public class AppController {

    @Resource
    private BackPaymentChannelService channelService;
    @Resource
    private FrontUserService frontUserService;
    @Resource
    private TransactionRecordService transactionRecordService;
    @Resource
    private BackSettingService settingService;
    @Resource
    private PromotionsService promotionsService;

    /**
     * 获取支付通道
     *
     * @return 返回结果
     */
    @SaCheckLogin
    @GetMapping("/channel")
    public ApiResult<List<Map<String, Object>>> channelList() {
        List<BackPaymentChannel> channels = channelService.lambdaQuery()
                .eq(BackPaymentChannel::getDefaultChannel, true)
                .eq(BackPaymentChannel::getChannel, 1)
                .list();
        return ApiResult.success(channels.stream()
                .map(c -> Putif.start()
                        .anyway("id", c.getId())
                        .anyway("name", c.getName())
                        .anyway("type", c.getType())
                        .anyway("rate", c.getRate())
                        .get())
                .collect(Collectors.toList()));
    }

    /**
     * 充值
     *
     * @param rechargeInfoReq 实体对象
     * @return 新增结果
     */
    @SaCheckLogin
    @PostMapping("recharge")
    public ApiResult<Object> recharge(@Validated(App.class) @RequestBody RechargeInfoReq rechargeInfoReq, HttpServletRequest request) {
        long userId = CurrentUserUtil.getUserId();
        FrontUser user = frontUserService.getById(userId);
        if (user == null || StrUtil.isBlank(user.getIdentityCard()) || StrUtil.isBlank(user.getName())) {
            BigDecimal amount = new BigDecimal(rechargeInfoReq.getAmount());
            if (NumberUtil.isGreater(amount, BigDecimal.valueOf(2000))) {
                throw new ServiceException("请先进行实名认证", ApiCode.NOT_REAL_NAME);
            }
            BigDecimal sum = transactionRecordService.todayRecord(userId);
            if (NumberUtil.isGreater(sum, BigDecimal.valueOf(5000))) {
                throw new ServiceException("请先进行实名认证", ApiCode.NOT_REAL_NAME);
            }
        }
        return ApiResult.success(transactionRecordService.recharge(rechargeInfoReq, request));
    }

    /**
     * 查询充值结果
     *
     * @param id 充值订单号
     * @return 返回结果
     */
    @SaCheckLogin
    @GetMapping("query/{id}")
    public ApiResult<Map<String, Object>> queryResult(@PathVariable long id) {
        if(id == 0) {
            return ApiResult.success("", Putif.start()
                            .anyway("result", 0)
                    .get());
        }
        Record record = transactionRecordService.getRecord(id);
        if (record == null) {
            return ApiResult.success("", Putif.start()
                    .anyway("result", 0)
                    .get());
        }

        return ApiResult.success("", Putif.start()
                .anyway("result", record.getState() == TransactionState.MANUAL ? 2 : record.getState().getCode())
                .get());
    }

    private void checkIsCertification(long userId) {
        FrontUser user = frontUserService.getById(userId);
        if (user == null || StrUtil.isBlank(user.getIdentityCard()) || StrUtil.isBlank(user.getPhone())) {
            throw new ServiceException("请先进行实名认证");
        }
        if (StrUtil.isBlank(user.getPhone())) {
            throw new ServiceException("请完善手机号");
        }
    }

    /**
     * 新增数据
     *
     * @param frontUser 实体对象
     * @return 新增结果
     * @author jzw
     * @since 2022-01-07 15:45:19
     */
    @PostMapping("insert")
    public ApiResult<Map<String, Object>> insert(@Validated(Save.class) @RequestBody FrontUser frontUser) {
        String key = SMSVerificationCode.REGIST_KEY + frontUser.getPhone();
        SaSession session = SaSessionCustomUtil.getSessionById(key);
        Assert.notNull(session, "验证码已失效");
        String c = session.getString("code");
        if (StrUtil.isBlank(c) || !c.equals(frontUser.getCode())) {
            return fail("验证码不正确");
        }
        session.clear();
        boolean isSuccess = frontUserService.insert(frontUser);
        //注册成功后自动登录
        if (isSuccess) {
            Long frontUserId = frontUser.getId();
            StpUtil.login(frontUserId, "APP");
            String account = frontUser.getAccount();
            String name = frontUser.getName();
            StpUtil.getSession().set(Constant.USER, new UserInfoForSession(frontUserId, account, name));
            Dict dict = Dict.create();
            dict.set("token", StpUtil.getTokenValue()).set("name", name).set("account", account).set("balance", frontUser.getBalance());
            return ApiResult.success(dict);
        }
        //return ApiResult.result(isSuccess);
        return ApiResult.fail();
    }

    /**
     * 登录
     *
     * @param loginInfoReq 存放账号和密码
     * @return cn.shop.utils.ApiResult<java.util.Map < java.lang.String, java.lang.Object>> 返回sessionid
     * @author jzw
     * @since 2021/11/2 14:11
     */
    @PostMapping("login")
    public ApiResult<Map<String, Object>> login(@Validated @RequestBody LoginInfoReq loginInfoReq, @RequestHeader(required = false, name = "Captcha-Token") String captchaToken) {
        Assert.notBlank(captchaToken, "captchaToken 不能为空");
        SaSession saSession = SaSessionCustomUtil.getSessionById(captchaToken, false);
        Assert.notNull(saSession, "验证码已失效");
        Object captchaSessionKey = saSession.get(Constant.CAPTCHA_SESSION_KEY);
        Assert.notNull(captchaSessionKey, "验证码已失效");
        Assert.isTrue(loginInfoReq.getCode().equalsIgnoreCase((String) captchaSessionKey), "验证码不正确");
        saSession.clear();
        LambdaQueryWrapper<FrontUser> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(FrontUser::getAccount, loginInfoReq.getAccount());
        FrontUser frontUser = frontUserService.getOne(wrapper);
        if (frontUser == null) {
            return ApiResult.fail("账户或密码错误");
        }
        if (frontUser.getRisk()) {
            return fail("账号已被禁止登录，请联系客服");
        }
        if (frontUser.getAdvanced()) {
            String key = Constant.SMS_CODE + ADV_CODE + frontUser.getPhone();
            SaSession session = SaSessionCustomUtil.getSessionById(key);
            Assert.notNull(session, "验证码已失效");
            String c = session.getString("code");
            if (StrUtil.isBlank(c) || !c.equals(loginInfoReq.getAdvcode())) {
                return fail("验证码不正确");
            }
            session.clear();
        }
        String password = frontUser.getPassword();
        Assert.notBlank(password, "密码不能为空");
        boolean b = DigestUtil.bcryptCheck(loginInfoReq.getPassword(), password);
        if (b) {
            Long frontUserId = frontUser.getId();
            StpUtil.login(frontUserId, "APP");
            String account = frontUser.getAccount();
            String name = frontUser.getName();
            StpUtil.getSession().set(Constant.USER, new UserInfoForSession(frontUserId, account, name));
            Dict dict = Dict.create();
            dict.set("token", StpUtil.getTokenValue()).set("name", name).set("account", account).set("balance", frontUser.getBalance());
            return ApiResult.success(dict);
        }
        return ApiResult.fail("账号或密码错误");
    }

    @PostMapping("/applogin")
    public ApiResult<Map<String, Object>> applogin(@Validated @RequestBody AppWxLogin req) {
        FrontUser wxuser = frontUserService.lambdaQuery()
                .eq(FrontUser::getUnionId, req.getUnionid())
                .one();
        if (wxuser == null) {
            wxuser = frontUserService.wxuserCreate(req.getHeadimgurl(), req.getUnionid());
        }
        FrontUser update = new FrontUser();
        update.setAvatar(wxuser.getAvatar());
        frontUserService.updateById(update);
        Long frontUserId = wxuser.getId();
        StpUtil.login(frontUserId, "APP");
        String account = wxuser.getAccount();
        String name = wxuser.getName();
        StpUtil.getSession().set(Constant.USER, new UserInfoForSession(frontUserId, account, name));
        Dict dict = Dict.create();
        dict.set("token", StpUtil.getTokenValue()).set("name", name).set("account", account).set("balance", wxuser.getBalance());
        return ApiResult.success(dict);
    }

    /**
     * 登出
     *
     * @return cn.shop.utils.ApiResult<java.util.Map < java.lang.String, java.lang.Object>>
     * @author jzw
     * @since 2021/11/2 14:11
     */
    @SaCheckLogin
    @GetMapping("logout")
    public ApiResult<Map<String, Object>> logout() {
        long uid = CurrentUserUtil.getUserId();
        StpUtil.logout(uid, "APP");
        return ApiResult.success();
    }

    /**
     * 获取app微信登陆开关
     *
     * @return
     */
    @GetMapping("/wxswitch")
    public ApiResult<Object> wxLoginSwitch() {
        BackSetting setting = settingService.findByKey(WX_LOGIN_SWITCH);
        return ApiResult.success((Object)setting.getValue());
    }

    /**
     * 设置是否自动提现
     *
     * @param user 是否自动提现
     * @return 返回结果
     */
    @SaCheckLogin
    @PutMapping("/setAutoWithdraw")
    public ApiResult<Map<String, Object>> updateAutoWithdraw(@Validated @RequestBody AutWithdrawReq user) {
        long userId = CurrentUserUtil.getUserId();
        return result(frontUserService.setAutoWithdraw(userId, user.getAutoWithdraw()));
    }

    /**
     * 获取app交易奖励说明和开关设置
     *
     * @return
     */
    @GetMapping("/reward")
    public ApiResult<Map<String, Object>> appReward() {
        Promotions promotions = promotionsService.lambdaQuery()
                .eq(Promotions::getKey, PromotionsConstant.APP_REWARD)
                .one();
        Map<String, Object> map = new HashMap<>();
        if (promotions == null) {
            map.put(REWARD_CONTENT, "");
            map.put(OPEN_REWARD, "0");
            map.put(REWARD_POOL, "0");
            return ApiResult.success(map);
        } else {
            map.put(REWARD_CONTENT, promotions.getContent());
            map.put(OPEN_REWARD, promotions.getEnable() ? "1" : "0");
            map.put(REWARD_POOL, promotions.getPrizePool());
        }
        return ApiResult.success(map);
    }
}
